﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace WenSkin
{
    public class Csv
    {
        public Csv()
        {
        }
        public Csv(string path)
        {
            LoadFile(path);
        }
        public Csv(DataTable dt)
        {
            Load(dt);
        }

        #region 私有属性

        public RowList rows;

        #endregion


        #region 公有属性
        public Encoding Encoding { get; set; } = Encoding.UTF8;
        public int HeaderLine { get; set; } = 1;
        public List<string> Columns { get; } = new List<string>();
        public RowList Rows => rows ??= new RowList(this);
        public string RemData { get; set; }
        public string Path { get; set; }
        public string Text { get => GetText(); set => Load(value); }
        public Row this[int index] => Rows[index];
        #endregion


        public void LoadFile(string path)
        {
            if (!File.Exists(path))
                throw new FileNotFoundException("访问文件不存在 : " + path);

            Path = path;
            Load(File.ReadAllText(path, Encoding));
        }

        public void Load(string text)
        {
            Rows.Clear();
            Columns.Clear();
            int index = 0;

            string rowStr = "";
            bool special = false;
            bool isFirst = true;


            foreach (var t in text.Replace("\r\n", "\n").Split('\n'))
            {
                string line = t;

                rowStr += line;
                //获取该行双引号的个数，如果是双数或为0，则为完整一行
                //如果该行双引号个数为单数，则是非完整行，需要下次出现单数才是完整行
                int remainder = (line.Split(new char[] { '"' }, StringSplitOptions.None).Length - 1) % 2;
                if (remainder != 0)
                {
                    if (special)
                    {
                        special = false;
                    }
                    else
                    {
                        rowStr += Environment.NewLine;
                        special = true;
                        continue;
                    }
                }
                else
                {
                    if (special)
                    {
                        rowStr += Environment.NewLine;
                        continue;
                    }
                }

                //解析完整行的csv数据
                string[] rowData = SplitCsvRow(rowStr);
                rowStr = null;
                if (isFirst && HeaderLine > 0)
                {
                    if (++index == HeaderLine)
                    {
                        isFirst = false;
                        foreach (string colName in rowData)
                        {
                            Columns.Add(colName);
                        }
                    }
                    else
                    {
                        RemData += rowData + "\n";
                    }
                }
                else
                {
                    Rows.Add(rowData);
                }
            }
            if (special)
            {
                throw new Exception("csv文件格式错误");
            }
        }

        public void Load(DataTable dt)
        {
            Rows.Clear();
            Columns.Clear();
            HeaderLine = 1;
            foreach (DataColumn col in dt.Columns)
            {
                Columns.Add(col.ColumnName);
            }
            foreach (DataRow row in dt.Rows)
            {

                Rows.Add(row.ItemArray);
            }
        }


        // 解析完整行的csv数据
        private static string[] SplitCsvRow(string rowText)
        {
            List<string> temp = new List<string>();
            if (rowText.IndexOf('"') < 0)
            {
                //无特殊字符，直接按逗号拆分
                temp.AddRange(rowText.Split(new char[] { ',' }));
            }
            else
            {
                var spArr = rowText.Split(new char[] { ',' });
                string cellValue = null;
                bool special = false;
                //有特殊字符
                foreach (var spItem in spArr)
                {
                    cellValue += spItem;
                    int remainder = (spItem.Split(new char[] { '"' }, StringSplitOptions.None).Length - 1) % 2;
                    if (remainder != 0)
                    {
                        if (special)
                        {
                            special = false;
                        }
                        else
                        {
                            cellValue += ',';
                            special = true;
                            continue;
                        }
                    }
                    else
                    {
                        if (special)
                        {
                            cellValue += ',';
                            continue;
                        }
                    }
                    if (special)
                    {
                        throw new Exception("csv文件行格式错误");
                    }
                    if (cellValue.Length > 2 && cellValue.Last() == '\"' && cellValue.First() == '\"')
                        temp.Add(cellValue.Remove(cellValue.Length - 1).Remove(0, 1).Replace("\"\"", "\""));
                    else
                        temp.Add(cellValue);
                    cellValue = null;
                }
            }
            return temp.ToArray();
        }

        public DataTable GetDataTable()
        {
            DataTable dt = new DataTable();

            if (Columns.Count > 0)
            {
                foreach (var item in Columns)
                {
                    dt.Columns.Add(item);
                }

                int cMax = Rows.Max(q => q.Count);

                while (cMax > dt.Columns.Count)
                {
                    dt.Columns.Add(dt.Columns.Count.ToString());
                }
            }
            else
            {
                int cMax = Rows.Max(q => q.Count);
                if (cMax > 0)
                {
                    for (int i = 0; i < cMax; i++)
                    {
                        dt.Columns.Add(i.ToString());
                    }
                }
            }


            foreach (var row in Rows)
            {
                dt.Rows.Add(row.ToArray());
            }
            return dt;
        }

        public string GetText()
        {
            StringBuilder sb = new StringBuilder();
            if (HeaderLine > 0)
            {
                if (string.IsNullOrWhiteSpace(RemData))
                    sb.Append(RemData);
                foreach (var item in Columns)
                {
                    if (item.Contains("\"") || item.Contains(","))
                        sb.Append($"\"{item.Replace("\"", "\"\"")}\",");
                    else
                        sb.Append(item + ",");
                }
                sb.Remove(sb.Length - 1, 1);
                sb.Append("\r\n");
            }
            foreach (var row in Rows)
            {
                foreach (var item in row)
                {
                    if (item.Contains("\"") || item.Contains(","))
                        sb.Append($"\"{item.Replace("\"", "\"\"")}\",");
                    else
                        sb.Append(item + ",");
                }
                sb.Remove(sb.Length - 1, 1);
                sb.Append("\r\n");
            }
            sb.Remove(sb.Length - 2, 2);
            return sb.ToString();
        }

        public void Save()
        {
            Save(Path);
        }

        public void Save(string path)
        {
            File.WriteAllText(path, GetText(), Encoding);
        }

        public class RowList : List<Row>
        {
            private readonly Csv owner;

            public RowList(Csv owner)
            {
                this.owner = owner;
            }
            public Csv Csv => owner;
            public Row Add(string[] s)
            {
                Row row = new Row(this);
                row.AddRange(s.ToList());
                Add(row);
                return row;
            }

            public Row Add(object[] s)
            {
                Row row = new Row(this);
                foreach (var item in s)
                {
                    row.Add(item?.ToString() ?? "");
                }
                Add(row);
                return row;
            }

            public Row Add(string str)
            {
                if (string.IsNullOrEmpty(str))
                {
                    return null;
                }
                Row row = new Row(this);
                row.AddRange(SplitCsvRow(str));
                Add(row);
                return row;
            }

            public Row Add()
            {
                Row row = new Row(this);
                for (int i = 0; i < owner.Columns.Count; i++)
                {
                    row.Add("");
                }
                Add(row);
                return row;
            }
        }

        public class Row : List<string>
        {
            public string this[string key]
            {
                get
                {
                    int index = owner.Csv.Columns.IndexOf(key);
                    if (index == -1 || index > Count - 1)
                    {
                        return null;
                    }
                    else
                    {
                        return this[index];
                    }
                }
                set
                {
                    int index = owner.Csv.Columns.IndexOf(key);
                    if (index == -1 || index > Count - 1)
                    {
                        return;
                    }
                    else
                    {
                        this[index] = value;
                    }
                }
            }

            public RowList owner;
            public Row(RowList owner)
            {
                this.owner = owner;
            }
        }

    }
}
